#ifndef __SIMTYPES_H__
#define __SIMTYPES_H__

#include <sys/types.h>

#include "base/simconstants.h"

#ifndef NULL
#define NULL 0
#endif

/* ***********************************************************
 * first make sure that we are compiling for one of the 
 * supported platforms 
 * ***********************************************************/

#if defined (i386) || defined (_AIX) || defined(__APPLE__) || defined(PPC)
#else
#error error1
#endif

/* ************************************************************
 * Integer data types. 
 * ************************************************************/

/* perhaps someday the hostnames could be replaced with more explicit
   flags such as the following "HOST_64". */
#if (defined(_AIX)) && defined(HOST_64) 
/* /usr/include/sys/inttypes.h defines the rest */
typedef unsigned char       uint8;
typedef unsigned short     uint16;
typedef unsigned           uint32;
typedef unsigned long long uint64;

#elif defined(i386) || defined(_AIX) || defined(__APPLE__) || defined(PPC)
#if !defined(_AIX) || !defined(_H_INTTYPES)
/* these are preempted by /usr/include/sys/inttypes.h on AIX 4.3 */
typedef signed char          int8;
typedef short               int16;
typedef int                 int32;
typedef long long           int64;
#endif
typedef unsigned char       uint8;
typedef unsigned short     uint16;
typedef unsigned           uint32;
typedef unsigned long long uint64;
#endif

#define VA_MASK_HIGH_BIT 0x7fffffffffffffffLL
#define VA_HIGH_BIT      0x8000000000000000LL
#define RegFormat "%.16llX"

#define PHYS_ADDR_SIZE sizeof(PA)

typedef enum {
    WORDACCESS_LOAD,
    WORDACCESS_STORE,
    DWORDACCESS_STORE,
    QWORDACCESS_STORE,
    BLOCKACCESS_LOAD,
    BLOCKACCESS_STORE,
} WordAccessType;

typedef enum Result {
    success,
    stall,
    failure
} Result;

/* ****************************************
 * conversion macros
 * ****************************************/

#ifdef HOST_64
typedef uint64 intPtrSize;
#else
typedef uint  intPtrSize;
#endif

#define PTR_TO_UINT(_x)     ((intPtrSize)(_x))
#define PTR_TO_UINT64(_x)   ((uint64)(intPtrSize)(_x))
#define UINT64_TO_PTR(_x)   ((void *)(intPtrSize)(_x))
#define UINT_TO_PTRSIZE(_x) ((intPtrSize)(_x))

#ifndef FALSE
#define FALSE         0
#endif

#ifndef TRUE
#define TRUE          1
#endif

/* TODO: above this is stuff that could stand some clean up 
 ************************************************************/

/* Global Types */
typedef unsigned char Byte;
typedef unsigned short Half;
typedef unsigned long Word;
typedef unsigned long long Address;
typedef unsigned long long Time;

typedef int Node;

typedef uint  Reg32;  /* Generic 32bit register type - must be unsigned*/
typedef int   Reg32_s;  /* Generic signed 32bit register type */
typedef uint64  Reg64;  /* Generic 64bit register type */
typedef int64   Reg64_s;  /* Generic 64bit register type */
typedef Reg64   Reg;  /* Generic register type */
typedef Reg64_s Reg_s; 
typedef Reg64   VA;   /* Virtual address */
typedef Reg64 PA;    /* Physical Address */
typedef struct _RegV {
  Reg64 hi;
  Reg64 lo;
} RegV;

#ifndef __cplusplus
typedef uint bool;
#endif

#define CAST_ASSERT(t, e)                                               \
    ({                                                                  \
        do {                                                            \
            if (dynamic_cast<t>(e) == NULL) {                           \
                Log->w("Cannot convert %p (%s*) to type %s (%s)\n",     \
                       e,   typeid(*e).name(),                          \
                       # t, typeid(t).name());                          \
                ASSERT(false);                                          \
            }                                                           \
        } while (0);                                                    \
        0;                                                              \
    })
#define CAST_OR_DIE(t, e) ((CAST_ASSERT(t,e)), (static_cast<t>(e)))

// IS_A will return true if p is pointer of type t, so if p points to
// a t or one if its children types, it will return true, i.e.
// Child* p; IS_A(Parent*, p); will return true.
#define IS_A(t, p) (dynamic_cast<t>(p) != NULL)

// IS_PTR_TO will return true if p points precisely to an object of
// type t.  If p points to a child of t, it will return false. i.e.
// Child* p; IS_PTR_TO(Parent, p); will return false.
#define IS_PTR_TO(t, p) (typeid(*p) == typeid(t)) 

// bdc: try to implement 32-bit compatability mode for effective addresses
// of loads and stores. see 970-software.pdf page 3 of 20.
static inline uint32 ADDR_UINT32 (VA addr) {
    return addr & 0xffffffff;
}
static inline uint64 ADDR_UINT64 (void* addr) {
    return ((uint32)addr);
}
static inline void* ADDR_VOIDSTAR (VA addr) {
    return (void*)ADDR_UINT32(addr);
}
static inline uint32* ADDR_UINT32STAR (VA addr) {
    return (uint32*)ADDR_VOIDSTAR(addr);
}
static inline uint64* ADDR_UINT64STAR (VA addr) {
    return (uint64*)ADDR_VOIDSTAR(addr);
}

#define HI(_v) ((uint32)(((uint64)(_v))>>32))
#define LO(_v) ((uint32)(_v))
extern uint64 mask64 (uint starting_bit, uint ending_bit);  /* bits in range 0..63 */
extern uint32 mask32 (uint starting_bit, uint ending_bit);  /* bits in range 0..31 */


/* 
   Use "type-punning" to avoid problem with -fstrict-aliases, as
   directed by GCC manual section "Options That Control Optimization"
*/
union typeconvert {
    double    d;
    long long ll;
    float      f[2];
};
__INLINE__ long long double2longlong (double d) {
    union typeconvert convert;
    convert.d = d;
    return convert.ll;
}
__INLINE__ double longlong2double (long long ll) {
    union typeconvert convert;
    convert.ll = ll;
    return convert.d;
}
__INLINE__ long long float2longlong (float f) {
    union typeconvert convert;
    convert.f[0] = 0;
    convert.f[1] = f;
    return convert.ll;
}
__INLINE__ float longlong2float (long long ll) {
    union typeconvert convert;
    convert.ll = ll;
    return convert.f[1];
}

#endif // __SIMTYPES_H__
